package netty;

import cn.hutool.core.date.DateUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.Scanner;

@Slf4j
public class NettyGroupChatCase {
    private static final  int PORT =6668;

    public static void main(String[] args) {
        NettyGroupChatCase chatCase = new NettyGroupChatCase();
        chatCase.groupClient();
    }
    
    @Test
    public void groupClient(){
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new StringDecoder());
                            //  自己的处理器
                            pipeline.addLast(new GroupClientHandle());
                            
                        }
                    });
            log.error("客户端 ok。。。");
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", PORT).sync();
            Channel channel = channelFuture.channel();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()){
                String msg = scanner.nextLine();
                channel.writeAndFlush(msg+"/r/n");
            }
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }

    }
    @Test
    public void groupServer(){
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(4);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128) // 设置线程队列连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true)// 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {
    
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new StringDecoder());
                            // 自己测处理器
                            pipeline.addLast(new GroupServerHandle());
                        }
                    });
            log.error("服务器 准备完成");
            
            ChannelFuture cf = serverBootstrap.bind(PORT).sync();
            
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
    
    private static class GroupClientHandle extends SimpleChannelInboundHandler<String>{

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            log.error("客户端发送的消息----{}",msg);
        }
    }
    
    private static class GroupServerHandle extends SimpleChannelInboundHandler<String>{

        // 定义一个channel组，管理所有的channel 。注意 GlobalEventExecutor.INSTANCE 是个单例，并且在javadoc 中说，是不适合大任务量的
        private static final ChannelGroup CHANNEL_GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


        /** 此方法是，当连接建立的时候被回调。然后需要将客户端连接信息，加入到 chanelGroup(并通知其他客户端）
         * @param ctx 
         * @throws Exception
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            // 此方法会自动将信息，推送给 CHANNEL_GROUP其他成功，这样你就不需要遍历了
            CHANNEL_GROUP.writeAndFlush("[客户端]:"+channel.remoteAddress()+"加入群聊\n"+ DateUtil.now());
            CHANNEL_GROUP.add(channel);
        }

        /**当channel 处于活动状态
         * @param ctx 
         * @throws Exception
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            log.error(ctx.channel().remoteAddress()+"上线了");
        }

        /**当处于非活动状态时，回调
         * @param ctx 
         * @throws Exception
         */
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {

            log.error(ctx.channel().remoteAddress()+"离线了~");
        }

        /**断开连接，回调
         * @param ctx 
         * @throws Exception
         */
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            // 通知所有人，xx下线了
            CHANNEL_GROUP.writeAndFlush("[客户端]:"+channel.remoteAddress()+"下线了~\n");
        }

        /**处理信息（此案例，需要把消息转发给其他1人）
         * @param ctx 
         * @param msg
         * @throws Exception
         */
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            Channel channel = ctx.channel();
            
            CHANNEL_GROUP.forEach(c->{
                if (c !=channel){
                    c.writeAndFlush("[客户]："+channel.remoteAddress()+"发送了："+msg+"\n");
                }else{
                    // 是自己
                    c.writeAndFlush("我自己发了一条消息："+msg);
                }
            });
            

        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();
        }
    }
    
}
